package com.xinxin.base.util.legacy;

import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by Chris_He on 2018/7/6.
 */
public class CommonUtil {
//    private static final Logger log = LoggerFactory.getLogger(CommonUtil.class);

    //    private static final String C_CODES_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$.`";
    private static final String C_CODES_STRING = "123456789ABCDEFGHJKMNPQRSTUVWXYZ";
    public static final String PREFIX_LIVE = "L";
    public static final String PREFIX_MALL = "D";
    public static final String PREFIX_MALL_ORDER = "MO"; //新版电商订单编号, 用于支付
    public static final String PREFIX_MALL_ORDER_ITEM = "MI"; //新版电商子订单编号, 用于支付
    public static final String PREFIX_DISCOUNT = "Y";
    public static final String PREFIX_BATTERY = "B";
    public static final String PREFIX_IPM = "I";
    public static final String PREFIX_ANYPAY = "A";
    public static final String DEV_URL = "https://testapi.ickapay.com";
    //    public static final String ONLINE_URL = "https://api.ickapay.com";
    public static final String ONLINE_URL = "http://stapi.ickapay.com";
    public static final boolean ONLINE = true;
    //    public static final boolean ONLINE = false;
    public static double EARTH_RADIUS = 6378.137;
    private static final int INADDR4SZ = 4;

    public static HashMap<String, String> objectToMap(Object obj) throws Exception {
        if (obj == null)
            return null;

        HashMap<String, String> map = new HashMap<>();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(obj) : null;
            map.put(key, null == value ? "" : value.toString());
        }

        return map;
    }

    public static Object mapToObject(HashMap<String, String> map, Class<?> beanClass) throws Exception {
        if (map == null)
            return null;

        Object obj = beanClass.newInstance();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            if (null != property.getPropertyType()) {
                System.out.println(property.getPropertyType().toString());
            }
            Method setter = property.getWriteMethod();
            if (setter != null && StringUtils.isNotBlank(map.get(property.getName()))) {
                try {
                    setter.invoke(obj, map.get(property.getName()).toString());
                } catch (IllegalArgumentException e) {
                    try {
                        setter.invoke(obj, Integer.valueOf(map.get(property.getName())));
                    } catch (IllegalArgumentException ee) {
                        try {
                            setter.invoke(obj, Long.valueOf(map.get(property.getName())));
                        } catch (IllegalArgumentException eee) {
                            try {
                                setter.invoke(obj, Double.valueOf(map.get(property.getName())));
                            } catch (IllegalArgumentException eeee) {
//                                log.info("类型转换失败:" + map.get(property.getName()));
                                System.out.println("类型转换失败:" + map.get(property.getName()));
                            }
                        }
                    }
                }
            }
        }

        return obj;
    }

    public static Map<String, Object> objToMap(Object obj) throws Exception {
        if (obj == null)
            return null;

        Map<String, Object> map = new HashMap<String, Object>();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(obj) : null;
            map.put(key, value);
        }

        return map;
    }

    public static byte[] intToBytes(int n) {
        byte[] b = new byte[4];

        for (int i = 0; i < 4; i++) {
            b[i] = (byte) (n >> (24 - i * 8));

        }
        return b;
    }

    //char转化为byte
    public static byte[] charToByte(char c) {
        byte[] b = new byte[2];
        b[0] = (byte) ((c & 0xFF00) >> 8);
        b[1] = (byte) (c & 0xFF);
        return b;
    }

    //byte转换为char
    public static char[] bytesToChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }

    // 验证是否是邮箱
    public static boolean isEmail(String mail) {
        Pattern pattern = Pattern
                .compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");
        Matcher matcher = pattern.matcher(mail);
        return matcher.matches();
    }

    public static String numberSwitch(int num, int base) {
        StringBuffer sb = new StringBuffer();
        String all = "123456789ABCDEFGHJKMNPQRSTUVWXYZ";
        String digths = all.substring(0, base);//将要转换的进制字母对应表
//只能装字符型的栈
        Stack s = new Stack();
        while (num != 0) {
// digths.charAt(n % base) 返回指定索引处的值
            s.push(digths.charAt(num % base));
            num /= base;
        }
        while (!s.isEmpty()) {
            sb.append(s.pop());
        }
//        if (sb.length() == 5) {
//            sb.insert(0, "L");
//        }
        return sb.toString();
    }

    /***
     * 将10进制转换为任意进制
     *
     * @param intVal
     * @param base   <=42
     * @return
     */
    public static String int2CodeString(long intVal, int base) {

        int w_code_len = C_CODES_STRING.length();
        if (base > w_code_len) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        Stack<String> s = new Stack<String>();
        while (intVal != 0) {
            s.push(C_CODES_STRING.charAt((int) (intVal % base)) + "");
            intVal /= base;
        }
        while (!s.empty()) {
            sb.append(s.pop());
        }
        if (sb.length() == 5) {
            sb.insert(0, "L");
        }
        return sb.length() == 0 ? null : sb.toString();
    }

    /***
     * 任何进制转换,
     *
     * @param s
     * @param srcBase  s的进制
     * @param destBase 要转换为的进制
     * @return
     */
    public static String baseConvert(String s, int srcBase, int destBase) {
        if (srcBase == destBase) {
            return s;
        }
        char[] chars = s.toCharArray();
        int len = chars.length;
        if (destBase != 10) {//目标进制不是十进制 先转化为十进制
            s = baseConvert(s, srcBase, 10);
        } else {
            long n = 0;
            for (int i = len - 1; i >= 0; i--) {
                n += C_CODES_STRING.indexOf(chars[i]) * Math.pow(srcBase, len - i - 1);
            }
            return String.valueOf(n);
        }
        return int2CodeString(Integer.valueOf(s), destBase);
    }

    /*
     * Converts IPv4 address in its textual presentation form
     * into its numeric binary form.
     *
     * @param src a String representing an IPv4 address in standard format
     * @return a byte array representing the IPv4 numeric address
     */
    public static byte[] textToNumericFormatV4(String src) {
        byte[] res = new byte[INADDR4SZ];

        long tmpValue = 0;
        int currByte = 0;
        boolean newOctet = true;

        int len = src.length();
        if (len == 0 || len > 15) {
            return null;
        }
        /*
         * When only one part is given, the value is stored directly in
         * the network address without any byte rearrangement.
         *
         * When a two part address is supplied, the last part is
         * interpreted as a 24-bit quantity and placed in the right
         * most three bytes of the network address. This makes the
         * two part address format convenient for specifying Class A
         * network addresses as net.host.
         *
         * When a three part address is specified, the last part is
         * interpreted as a 16-bit quantity and placed in the right
         * most two bytes of the network address. This makes the
         * three part address format convenient for specifying
         * Class B net- work addresses as 128.net.host.
         *
         * When four parts are specified, each is interpreted as a
         * byte of data and assigned, from left to right, to the
         * four bytes of an IPv4 address.
         *
         * We determine and parse the leading parts, if any, as single
         * byte values in one pass directly into the resulting byte[],
         * then the remainder is treated as a 8-to-32-bit entity and
         * translated into the remaining bytes in the array.
         */
        for (int i = 0; i < len; i++) {
            char c = src.charAt(i);
            if (c == '.') {
                if (newOctet || tmpValue < 0 || tmpValue > 0xff || currByte == 3) {
                    return null;
                }
                res[currByte++] = (byte) (tmpValue & 0xff);
                tmpValue = 0;
                newOctet = true;
            } else {
                int digit = Character.digit(c, 10);
                if (digit < 0) {
                    return null;
                }
                tmpValue *= 10;
                tmpValue += digit;
                newOctet = false;
            }
        }
        if (newOctet || tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {
            return null;
        }
        switch (currByte) {
            case 0:
                res[0] = (byte) ((tmpValue >> 24) & 0xff);
            case 1:
                res[1] = (byte) ((tmpValue >> 16) & 0xff);
            case 2:
                res[2] = (byte) ((tmpValue >> 8) & 0xff);
            case 3:
                res[3] = (byte) ((tmpValue >> 0) & 0xff);
        }
        return res;
    }

    public static boolean internalIp(String ip) {
        byte[] addr = textToNumericFormatV4(ip);
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        //10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        //172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        //192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                switch (b1) {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }

    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        ip = ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
        return ip;
    }

    //人民币转马币
    public static BigDecimal CNYtoMYR(BigDecimal USDtoCNY, BigDecimal USDtoMYR, BigDecimal cny) {
        BigDecimal myr = new BigDecimal("0");
        if (null == cny) {
            return myr;
        }
        myr = cny.multiply(USDtoMYR).divide(USDtoCNY, 2, BigDecimal.ROUND_HALF_UP);
        return myr;
    }

    //美元转马币
    public static BigDecimal USDtoMYR(BigDecimal USDtoMYR, BigDecimal usd) {
        BigDecimal myr = new BigDecimal("0");
        if (null == usd) {
            return myr;
        }
        myr = usd.multiply(USDtoMYR).setScale(2, BigDecimal.ROUND_HALF_UP);
        return myr;
    }

    //美元转人民币
    public static BigDecimal USDtoCNY(BigDecimal USDtoCNY, BigDecimal usd) {
        BigDecimal cny = new BigDecimal("0");
        if (null == usd) {
            return cny;
        }
        cny = usd.multiply(USDtoCNY).setScale(2, BigDecimal.ROUND_HALF_UP);
        return cny;
    }

    //马币转人民币
    public static BigDecimal MYRtoCNY(BigDecimal USDtoCNY, BigDecimal USDtoMYR, BigDecimal myr) {
        BigDecimal cny = new BigDecimal("0");
        if (null == myr) {
            return cny;
        }
        cny = myr.multiply(USDtoCNY).divide(USDtoMYR, 2, BigDecimal.ROUND_HALF_UP);
        return cny;
    }

    //马币转人民币
    public static BigDecimal MYRtoCNY(BigDecimal MYRtoCNY, BigDecimal myr) {
        BigDecimal cny = BigDecimal.ZERO;
        if (null == myr) {
            return cny;
        }

        cny = myr.multiply(MYRtoCNY).setScale(2, BigDecimal.ROUND_HALF_UP);
        return cny;
    }


    //人民币转美元
    public static BigDecimal CNYtoUSD(BigDecimal USDtoCNY, BigDecimal cny) {
        BigDecimal usd = new BigDecimal("0");
        if (null == cny) {
            return usd;
        }
        usd = cny.divide(USDtoCNY, 2, BigDecimal.ROUND_HALF_UP);
        return usd;
    }

    //马币转美元
    public static BigDecimal MYRtoUSD(BigDecimal USDtoMYR, BigDecimal myr) {
        BigDecimal usd = new BigDecimal("0");
        if (null == myr) {
            return usd;
        }
        usd = myr.divide(USDtoMYR, 2, BigDecimal.ROUND_HALF_UP);
        return usd;
    }

    public static boolean isNotBlank(CharSequence cs) {
        return !org.apache.commons.lang3.StringUtils.isBlank(cs) && !("null").equals(cs) && !("undefined").equals(cs);
    }

    public static boolean isBlank(CharSequence cs) {
        return org.apache.commons.lang3.StringUtils.isBlank(cs) || ("null").equals(cs) || ("undefined").equals(cs);
    }

    public static String getUniqueNo() {
        Integer pass = new Random().nextInt(99999);
        if (pass < 10000) pass += 10000;
        return String.valueOf(System.currentTimeMillis()) + pass.toString();
    }

    public static String getBillNo(String prefix) {
        //格式化当前时间
        SimpleDateFormat sfDate = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String strDate = sfDate.format(new Date());

        //为了防止高并发重复,再获取4个随机数
        Integer random = new Random().nextInt(9999);
        if (random < 1000) random += 1000;

        String billNo = prefix + strDate + random;
        //最后得到20位订单编号。

        return billNo;
    }

    public static String getDistance(String lon1, String lat1, String lon2, String lat2) {
        if (isBlank(lon1) || isBlank(lat1) || isBlank(lon2) || isBlank(lat2)) {
            return "";
        }
        return getDistance(Double.valueOf(lon1), Double.valueOf(lat1), Double.valueOf(lon2), Double.valueOf(lat2));
    }

    public static boolean isBlank(String str) {
        return !isNotBlank(str);
    }

    public static boolean isNotBlank(String str) {
        return !StringUtils.isBlank(str) && !"null".equals(str);
    }

    public static String getDistance(Double lon1, Double lat1, Double lon2, Double lat2) {
        if (null == lon1 || null == lat1 || null == lat2 || null == lon2) {
            return "";
        }
        // 计算两点间距离公式
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lon1) - rad(lon2);
        double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        distance = distance * EARTH_RADIUS;
        distance = Math.round(distance * 10000d) / 10000d;
        distance = distance * 1000;
        BigDecimal bd = new BigDecimal(String.valueOf(distance)).setScale(2, BigDecimal.ROUND_HALF_UP);
        return String.valueOf(bd.toString());
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    public static void main(String[] args) {
//        String key = "aZkrKVS63RDci+6hcq0RAJCxGt/EO3LF6eGqbMLjQjLFu8zGfq1mlK3FaX9KyKs1meTbmmoEGcxDz96PqcJQPpFqIZPR40BHKGobQgKNHAIbYieQKvhd/w==";
//             key = URLEncoder.encode(key);
//        System.out.println(key);
//        System.out.println(DESUtils.decodeKeyArray(key));

        //匹配6位顺增
        String pattern = "(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){6}\\d";
        Pattern pa = Pattern.compile(pattern);
        String mc = "123456789";
        Matcher ma = pa.matcher(mc);
        System.out.println("6位顺增 ：" + ma.matches());
        System.out.println("*******分割线*******");

        //匹配6位顺降
        pattern = "(?:9(?=8)|8(?=7)|7(?=6)|6(?=5)|5(?=4)|4(?=3)|3(?=2)|2(?=1)|1(?=0)){5}\\d";
        pa = Pattern.compile(pattern);
        mc = "654321";
        ma = pa.matcher(mc);
        System.out.println("6位顺降 ：" + ma.matches());
        System.out.println("*******分割线*******");

        //匹配6位顺增或顺降
        pattern = "(?:(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){5}|(?:9(?=8)|8(?=7)|7(?=6)|6(?=5)|5(?=4)|4(?=3)|3(?=2)|2(?=1)|1(?=0)){5})\\d";
        pa = Pattern.compile(pattern);
        mc = "234567";
        ma = pa.matcher(mc);
        System.out.println("6位顺增或顺降 ：" + ma.matches());
        System.out.println("*******分割线*******");

        //匹配4-9位连续的数字
        pattern = "(?:(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){3,}|(?:9(?=8)|8(?=7)|7(?=6)|6(?=5)|5(?=4)|4(?=3)|3(?=2)|2(?=1)|1(?=0)){3,})\\d";
        pa = Pattern.compile(pattern);
        mc = "123456789";
        ma = pa.matcher(mc);
        System.out.println("4-9位连续的数字 ：" + ma.matches());
        System.out.println("*******分割线*******");

        //匹配3位以上的重复数字
        pattern = "([\\d])\\1{2,}";
        pa = Pattern.compile(pattern);
        mc = "888888";
        ma = pa.matcher(mc);
        System.out.println("3位以上的重复数字 ：" + ma.matches());
        System.out.println("*******分割线*******");

        //匹配手机号码类
        pattern = "(13[0-9]|15[0-9]|18[0-9])([\\d]{2,4}){2}";
        pa = Pattern.compile(pattern);
        mc = "18930333033";
        ma = pa.matcher(mc);
        System.out.println("手机号码 ：" + ma.matches());
        System.out.println("*******分割线*******");

//                //匹配连同号如“112233”“222333”
//                pattern = "([\\d])\1{1,}([\\d])\\2{2,}"; //这个测试没有通过，要重新编写
//                pa = Pattern.compile(pattern);
//                mc = "33111";
//                ma = pa.matcher(mc);
//                System.out.println("连同号如112233 , 222333 ：" + ma.matches());
        System.out.println("*******分割线*******");

        //匹配2233类型
        pattern = "([\\d])\\1{1,}([\\d])\\2{1,}";
        pa = Pattern.compile(pattern);
        mc = "2233";
        ma = pa.matcher(mc);
        System.out.println("2233类型 ：" + ma.matches());
        System.out.println("*******分割线*******");

        System.out.println(int2CodeString(20010006, 32));
        System.out.println(baseConvert("M3CA5", 32, 10));
//        System.out.println(baseConvert("LM3CA5",10, 32));
        System.out.println("L2VBO7V".substring(1));

        String inviteCode = "LM3KZ9";
        if (inviteCode.startsWith("L")) {
            inviteCode = inviteCode.substring(1);
        }
        System.out.println(Long.valueOf(baseConvert(inviteCode, 32, 10)));
    }

    public static String listToString(List<Integer> integers) {
        String ids = "";
        if (null != integers && integers.size() > 0) {
            StringBuffer stringBuffer = new StringBuffer();
            for (Integer id : integers) {
                stringBuffer.append(id).append(",");
            }
            ids = stringBuffer.substring(0, stringBuffer.length() - 1);
        }
        return ids;
    }

    public static String longListToString(List<Long> integers) {
        String ids = "";
        if (null != integers && integers.size() > 0) {
            StringBuffer stringBuffer = new StringBuffer();
            for (Long id : integers) {
                stringBuffer.append(id).append(",");
            }
            ids = stringBuffer.substring(0, stringBuffer.length() - 1);
        }
        return ids;
    }

    public static String stringListToString(List<String> strings) {
        String ids = "";
        if (null != strings && strings.size() > 0) {
            StringBuffer stringBuffer = new StringBuffer();
            for (String s : strings) {
                stringBuffer.append(s).append(",");
            }
            ids = stringBuffer.substring(0, stringBuffer.length() - 1);
        }
        return ids;
    }

}
